﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Security;

namespace Emlyn.AudioToVideo.JobProcessorLib.core.SimpleAudioToVideoImpl
{
    public delegate void VoidDelegate();

    public class SimpleAudioToVideoJob: JobBase
    {
        Timer _timer;
        long _estimatedOutputFileSize;
        System.Diagnostics.Process _process;
        object _syncObj;

        public SimpleAudioToVideoJob() : base() { Init();  }

        public SimpleAudioToVideoJob(string aJobID) : base(aJobID) { Init(); }

        private void Init()
        {
            _timer = new Timer(new TimerCallback(OnTimer), null, Timeout.Infinite, Timeout.Infinite);
            _estimatedOutputFileSize = 0;
            _process = null;
            _syncObj = new object();
        }

        private string GetJobDetailsRow(int aRowNumber)
        {
            string retval = null;

            if (JobDetails != null)
            {
                using (StringReader sr = new StringReader(JobDetails))
                {
                    int lcurrentRow = 0;
                    string lline;
                    while ((lline = sr.ReadLine()) != null)
                    {
                        if (lcurrentRow == aRowNumber)
                        {
                            retval = lline;
                            break;
                        }
                        else
                            lcurrentRow++;
                    }
                }
            }

            return retval;
        }

        public static string AddBaseFolder(string aFileName)
        {
            string lpath1 = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            string lpath2 = Path.Combine(lpath1, "SimpleAudioToVideoJob");
            
            if (!Directory.Exists(lpath2))
                Directory.CreateDirectory(lpath2);

            return Path.Combine(lpath2, aFileName);
        }

        public string AudioFileName
        {
            get
            {
                string retval = GetJobDetailsRow(0);
                return retval;
            }
        }

        public string ImageFileName
        {
            get
            {
                string retval = GetJobDetailsRow(1);
                return retval;
            }
        }

        public string OutputFileName
        {
            get
            {
                string retval = GetJobDetailsRow(2);
                return retval;
            }
        }

        protected override bool CanStart()
        {
            bool retval =
                !String.IsNullOrEmpty(AudioFileName) &&
                !String.IsNullOrEmpty(ImageFileName) &&
                !String.IsNullOrEmpty(OutputFileName) &&
                File.Exists(AddBaseFolder(AudioFileName)) &&
                File.Exists(AddBaseFolder(ImageFileName)) ; //&& 
//                !File.Exists(AddBaseFolder(OutputFileName));

            return retval;
        }

        protected override void StartJob()
        {
            if (CanStart())
            {
                double lmp3durationSecs = GetMp3Duration(AddBaseFolder(AudioFileName)).TotalSeconds;

                string lmencoderCmd = "mencoder";
                string lmencoderArgs =
                    "\"mf://" + AddBaseFolder(ImageFileName) + "\" " +
                    "-mf fps=" + (1/lmp3durationSecs).ToString() + " -vf harddup -ovc lavc " +
                    "-lavcopts vbitrate=100 -audiofile \"" + AddBaseFolder(AudioFileName) + "\" -oac copy -ofps 30 " +
                    "-o \"" + AddBaseFolder(OutputFileName) + "\"";

                File.WriteAllText(AddBaseFolder("cmd.txt"), lmencoderCmd + " " + lmencoderArgs);

                lock (_syncObj)
                {
                    _process = new System.Diagnostics.Process();
                    _process.StartInfo.FileName = lmencoderCmd;
                    _process.StartInfo.Arguments = lmencoderArgs;
                    _process.Start();
                }

                VoidDelegate ldelegate = new VoidDelegate(WaitForProcessEnd);

                ldelegate.BeginInvoke(null, null);
            }
            else
                throw new Emlyn.AudioToVideo.JobProcessorLib.Interfaces.Exceptions.JobCannotStartException("Base folder is " + AddBaseFolder(""));
        }

        long CalculateEstimatedOutputFileSize()
        {
            string laudioFileFullPath = AddBaseFolder(AudioFileName);

            FileInfo laudioFileInfo = new FileInfo(laudioFileFullPath);

            long retval = (laudioFileInfo.Length + 1) * 2;

            return retval;
        }

        void WaitForProcessEnd()
        {
            try
            {
                _estimatedOutputFileSize = CalculateEstimatedOutputFileSize();

                _timer.Change(100, 1000);

                _process.WaitForExit(600000);

                _timer.Change(Timeout.Infinite, Timeout.Infinite);

                if (!_process.HasExited)
                {
                    SetErrorMessage("Timed Out");
                    SignalFailure();
                }
                else
                {
                    Progress = 100;
                    SignalSuccess();
                }

                lock (_syncObj)
                {
                    _process.Dispose();
                    _process = null;
                }
            }
            catch (Exception ex)
            {
                SetErrorMessage(ex.Message);
                SignalFailure();
            }
        }

        void OnTimer(object aSender)
        {
            try
            {
                string loutputFileFullPath = AddBaseFolder(OutputFileName);

                if (File.Exists(loutputFileFullPath))
                {
                    FileInfo loutFileInfo = new FileInfo(loutputFileFullPath);

                    //
                    File.AppendAllText(AddBaseFolder("tst.txt"), loutFileInfo.Length.ToString() + ", " + _estimatedOutputFileSize.ToString() + Environment.NewLine);

                    double lprogressRatio = (double)loutFileInfo.Length / (double)Math.Max(1, _estimatedOutputFileSize);

                    File.AppendAllText(AddBaseFolder("tst.txt"), ", Progress=" + lprogressRatio.ToString() + "% " + Environment.NewLine);

                    Progress = (Int32)Math.Truncate(lprogressRatio * 100);
                }
                else
                {
                    Progress = 0;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Fail(ex.ToString());
            }
        }

        //void WaitForProcessEndTerminates(IAsyncResult iar)
        //{
        //    AsyncResult lar = iar as AsyncResult;

        //    (lar.AsyncDelegate as ProcessDelegate).EndInvoke(iar);


        //}

        protected override void StopJob(bool aHalting)
        {
            lock (_syncObj)
            {
                if (_process != null)
                {
                    _process.Kill();
                }
            }
        }

        public TimeSpan GetMp3Duration(string aMp3FileNameFullPath)
        {
            TagLib.File file = TagLib.File.Create (aMp3FileNameFullPath);

            return file.Properties.Duration;
        }
    }
}
